﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Dotnet.Utils.Utility.ObjectExtensions
{
    public sealed class FieldSnapShot
    {
        private FieldSnapShot(object obj)
        {
            snapFields = GetFieldValues(obj.GetType(), obj);
        }

        public static FieldSnapShot Create(object obj)
        {
            if (obj == null)
            {
                throw new NullReferenceException("FieldSnapShot Oject Is Not Null");
            }
            return new FieldSnapShot(obj);
        }

        public void ApplyTo(object obj)
        {
            SetFieldValues(snapFields, obj.GetType(), obj);
        }

        internal static Dictionary<string, object> GetFieldValues(Type type, object obj)
        {
            Dictionary<string, object> dictionary = new Dictionary<string, object>();
            Type type2 = type;
            while (type2 != typeof(object))
            {
                FieldInfo[] fields = type2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (FieldInfo fieldInfo in fields)
                {
                    string key = string.Format("{0}:{1}", type2, fieldInfo.Name);
                    dictionary.Add(key, fieldInfo.GetValue(obj));
                }
                type2 = type2.BaseType;
            }
            return dictionary;
        }

        internal static void SetFieldValues(Dictionary<string, object> fieldValues, Type type, object obj)
        {
            Type type2 = type;
            while (type2 != typeof(object))
            {
                FieldInfo[] fields = type2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (FieldInfo fieldInfo in fields)
                {
                    string name = fieldInfo.Name;
                    string key = string.Format("{0}:{1}", type2, fieldInfo.Name);
                    if (fieldValues.ContainsKey(key))
                    {
                        object value = fieldValues[key];
                        fieldInfo.SetValue(obj, value);
                    }
                }
                type2 = type2.BaseType;
            }
        }

        private Dictionary<string, object> snapFields;
    }
}